/*
 * REFNetworkAddress.cpp
 *
 *  Created on: Apr 14, 2013
 *      Author: Lucifer
 */

#include "../pch.h"

namespace BFX
{

/**
 * Initializes a new instance of the REFInternetAddress class with the specified address and port number.
 * @param[in] strAddress The network address, a dotted number such as "127.0.0.1".
 *	Passing the empty string for this parameter indicates the socket should listen for client activity on all network interfaces.
 * @param[in] nPort The port identifying the socket application.
 */
REFInternetAddress::REFInternetAddress(const String& strAddress, int nPort)
{
	BFX_ENSURE1(nPort > MIN_PORT && nPort < MAX_PORT, "The specified port number is out of range.");

	m_lastError = SocketError_Success;

	m_strAddress = strAddress;
	m_nPort = nPort;

	// the cache data will generate on first time access.
	m_nSockAddrLen = 0;
	m_pSockAddr = NULL;
}

/**
 * Initializes a new instance of the REFInternetAddress class with the specified address and port number.
 * @param[in] pSockAddr A pointer to a SOCKADDR structure that contains the address to assign to this object.
 * @param[in] nSockAddrLen The length of the address in pSockAddr in bytes.
 */
REFInternetAddress::REFInternetAddress(const SOCKADDR* pSockAddr, int nSockAddrLen)
{
	BFX_ENSURE1(nSockAddrLen == sizeof(SOCKADDR_IN), "Invalid type of SOCKADDR structure, SOCKADDR_IN required.");
	BFX_ENSURE1(pSockAddr->sa_family == SocketAddressFamily_InterNetwork, "Bad address family, AF_INET required.");

	m_lastError = SocketError_Success;

	const SOCKADDR_IN* pSockAddrIn = (const SOCKADDR_IN*) pSockAddr;
	m_strAddress = inet_ntoa(pSockAddrIn->sin_addr);
	m_nPort = ntohs(pSockAddrIn->sin_port);

	// duplicates the cache data.
	m_nSockAddrLen = sizeof(SOCKADDR_IN);
	m_pSockAddr = new BYTE[m_nSockAddrLen];
	memcpy(m_pSockAddr, pSockAddr, m_nSockAddrLen);
}

/**
 * Destroys the instance of the REFInternetAddress class.
 */
REFInternetAddress::~REFInternetAddress()
{
	InvalidateCache();
}

//
// Attributes
//

/**
 * Gets the IP address.
 * @return A string that containing the IP address.
 */
String REFInternetAddress::GetAddress() const
{
	return m_strAddress;
}

/**
 * Sets the IP address.
 * @param[in] strAddress A string that containing the IP address.
 */
void REFInternetAddress::SetAddress(const String& strAddress)
{
	m_strAddress = strAddress;
	InvalidateCache();
}

/**
 * Gets the port number
 * @return An integer value in the range 0x00000000 to 0x0000FFFF indicating the port number.
 */
int REFInternetAddress::GetPort() const
{
	return m_nPort;
}

/**
 * Sets the port number
 * @param[in] nPort An integer value in the range 0x00000000 to 0x0000FFFF indicating the port number.
 */
void REFInternetAddress::SetPort(int nPort)
{
	BFX_ENSURE1(nPort > MIN_PORT && nPort < MAX_PORT, "port number out of range."); 
	m_nPort = nPort;
	InvalidateCache();
}

//
// REFSocketAddress overrides
//

/**
 * Gets the Internet Protocol (IP) address family.
 * @return Should be SocketAddressFamily_InterNetwork.
 */
SocketAddressFamily REFInternetAddress::GetAddressFamily() const
{
	return SocketAddressFamily_InterNetwork;
}

/**
 * Gets the pointer and length to the raw SOCKADDR structure.
 * @param[out] nSockAddrLen The length of the SOCKADDR structure, in bytes.
 * @return The pointer of the SOCKADDR structure that contains the address to assign to this object.
 */
const SOCKADDR* REFInternetAddress::GetSockAddr( int& nSockAddrLen) const
{
	if (m_pSockAddr == NULL)
	{
		// regenerate cache data.
		m_nSockAddrLen = sizeof(SOCKADDR_IN);
		m_pSockAddr = new BYTE[m_nSockAddrLen];
		memset(m_pSockAddr, 0, m_nSockAddrLen);

		SOCKADDR_IN* pSockAddr = (SOCKADDR_IN*)m_pSockAddr;
		pSockAddr->sin_family = SocketAddressFamily_InterNetwork;
		if (m_strAddress.IsEmpty())
			pSockAddr->sin_addr.s_addr = htonl(INADDR_ANY);
		else
		{
			UINT32 nAddress = inet_addr(m_strAddress);

			BFX_ASSERT1(nAddress != INADDR_NONE, "Bad address");
			if (nAddress == INADDR_NONE)
			{
				// TODO: Should we make a contract here, instead to set the last error.
				delete[] m_pSockAddr;
				m_pSockAddr = NULL;
				m_nSockAddrLen = 0;
				m_lastError = SocketError_Fault;

				nSockAddrLen = 0;
				InvalidateCache();
				return NULL;
			}
			pSockAddr->sin_addr.s_addr = nAddress;
		}
		pSockAddr->sin_port = htons((u_short)m_nPort);
	}

	BFX_ASSERT(m_nSockAddrLen == sizeof(SOCKADDR_IN));
	nSockAddrLen = m_nSockAddrLen;
	return (SOCKADDR*)m_pSockAddr;
}

/**
 * Creates a new instance of the BFX::REFSocketAddress class from a SOCKADDR structure.
 * @param[in] pSockAddr A pointer to a SOCKADDR structure that contains the address to assign to.
 * @param[in] nSockAddrLen The length of the address in pSockAddr in bytes.
 * @return A new BFX::REFSocketAddress instance that is initialized from the specified SOCKADDR structure.
	 */
REFSocketAddress* REFInternetAddress::Create(const SOCKADDR* pSockAddr, int nSockAddrLen) const
{
	REFSocketAddress* pNewAddress = new REFInternetAddress(pSockAddr, nSockAddrLen);
	pNewAddress->Autorelease();
	return pNewAddress;
}

//
// REFObject overrides
//

/**
 *
 * @return
 */
bool REFInternetAddress::IsValid() const
{
	return true; // ??? make a rule...
}

// The helper method uses to invalidate our cache data, when address or port number been updated.
void REFInternetAddress::InvalidateCache() const
{
	if (m_pSockAddr != NULL)
	{
		delete[] (BYTE*)m_pSockAddr;
		m_pSockAddr = NULL;
	}
	m_nSockAddrLen = 0;
}

} /* namespace BFX */
